#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <sys/wait.h>
#include <ctime>
#include <fstream>
#include <sys/types.h>
#include <sstream>
#include <filesystem>
#include <unistd.h>
using namespace std;

class Headler
{
public:
    Headler() : _enable(true)
    {
    }
    virtual ~Headler() = default;
    virtual void Excute(string &in) = 0;
    void SetNext(shared_ptr<Headler> next)
    {
        _next = next;
    }
    void Enable() { _enable = true; }
    void Disable() { _enable = false; }
    bool IsEnable() { return _enable; }

protected:
    shared_ptr<Headler> _next;
    bool _enable;
};

class TextFormat : public Headler
{
public:
    void Excute(string &in) override
    {
        if (Headler::IsEnable())
        {
            cout << "Format..." << endl;
            stringstream ss;
            ss << time(nullptr) << " - " << getpid() << endl;
            in = ss.str();
            sleep(1);
        }
        if (_next)
        {
            _next->Excute(in);
        }
        else
        {
            cout << "责任链结点结束，处理完成" << endl;
        }
    }
};

string path = "./tmp/";
string name = "text.log";

class TextFile : public Headler
{
public:
    TextFile() : _path(path), _name(name)
    {
        if (filesystem::exists(_path))
            return;
        try
        {
            filesystem::create_directories(_path);
        }
        catch (filesystem::filesystem_error &e)
        {
            cerr << e.what() << endl;
        }
    }
    ~TextFile()
    {
    }
    void Excute(string &in) override
    {
        if (Headler::IsEnable())
        {
            cout << "Save ..." << endl;
            sleep(1);
            const string file = _path + _name;
            ofstream out(file, ios::app);
            if (!out.is_open())
                return;
            out << in;
            out.close();
        }
        if (_next)
        {
            _next->Excute(in);
        }
        else
        {
            cout << "责任链结点结束，处理完成" << endl;
        }
    }

private:
    string _path;
    string _name;
};

const int maxline = 5;

class TextBackup : public Headler
{
public:
    TextBackup() : _path(path), _name(name), _maxline(maxline)
    {
    }
    ~TextBackup()
    {
    }
    void Excute(string &in) override
    {
        if (Headler::IsEnable())
        {
            cout << "Backup ..." << endl;
            sleep(1);
            const string filename = _path + _name;
            // 1. 打开⽂件
            ifstream inf(filename);
            if (!inf.is_open())
                return;
            std::string line;
            int currentlines = 0;
            while (getline(inf, line))
            {
                currentlines++;
            }
            // 关闭⽂件流
            inf.close();
            // 2. 备份
            if (currentlines > _maxline)
            {
                cout << "消息⾏数超过" << _maxline << ", 触发⽇志备份 " << endl;
                // ⼤于才做备份，否则什么⾛不做
                Backup();
            }
        }
        if (_next)
        {
            _next->Excute(in);
        }
        else
        {
            cout << "责任链结点结束，处理完成" << endl;
        }
    }
    void Backup()
    {
        string newname = _name + "." + to_string(time(nullptr));
        pid_t id = fork();
        if (id == 0)
        {
            chdir(_path.c_str());               // 更改进程路径，进⼊"./tmp/"路径下
            filesystem::rename(_name, newname); // rename⽐较快，也不影响未来其他继续写⼊的操作，因为会重新形成⽂件
            string tarname = newname + ".tgz";
            // ⼦进程打包备份
            cout << "打包 : " << newname << " 成为: " << tarname << "开始" << endl;
            execlp("tar", "tar", "czf", tarname.c_str(), newname.c_str(), nullptr); // 注意这⾥要以nullptr结尾，注意这⾥的坑
            cout << "打包 : " << newname << " 成为: " << tarname << "失败" << endl;
            exit(1);
        }
        waitpid(id, nullptr, 0);
        string tempfile = _path + newname;
        filesystem::remove(tempfile); // 删除⽂件原件，只要tar包
    }

private:
    int _maxline;
    string _path;
    string _name;
};

class HeadlerEntry
{
public:
    HeadlerEntry()
    {
        _format = make_shared<TextFormat>();
        _save = make_shared<TextFile>();
        _backup = make_shared<TextBackup>();

        _format->SetNext(_save);
        _save->SetNext(_backup);
    }
    void Run(string &in)
    {
        _format->Excute(in);
    }
    void EnableHandler(bool isformat, bool issave, bool isbackup)
    {
        isformat ? _format->Enable() : _format->Disable();
        issave ? _save->Enable() : _save->Disable();
        isbackup ? _backup->Enable() : _backup->Disable();
    }

private:
    shared_ptr<Headler> _format;
    shared_ptr<Headler> _save;
    shared_ptr<Headler> _backup;
};